﻿using System;
using System.Collections.ObjectModel;
using ParserGenerator.Output;
using ParserGenerator.SyntacticAnalysis;

namespace ParserGenerator.CodeGeneration
{
    [Serializable]
    public class CSharpSemanticActionWriter : CSharpProgramDescriptionWriter
    {
        public GrammarDeclaration Grammar { get; }

        public bool UseStringResources { get; }
        public bool UseFullyQualifiedNamespace { get; }

        public string StringResourcesNamespace { get; }
        public Identifier StringResourcesClassName { get; }

        public Identifier InvalidTokenResourceName { get; }
        public string InvalidTokenResourceText { get; }

        internal CSharpSemanticActionWriter(GrammarDeclaration grammar)
            : base(grammar)
        {
            if (grammar == null)
                throw new ArgumentNullException(nameof(grammar));
            Grammar = grammar;

            UseStringResources = grammar.GetOptionValue("pg.stringresources.enable", true);
            UseFullyQualifiedNamespace = grammar.GetOptionValue("pg.fullyqualified.namespace", true);

            StringResourcesNamespace = grammar.GetOptionValue("pg.stringresources.namespace", "ParserGenerator");
            StringResourcesClassName = Identifier.New(grammar.GetOptionValue("pg.stringresources.classname", "SR"));

            InvalidTokenResourceName = Identifier.New(grammar.GetOptionValue("pg.scanner.sr.InvalidToken.name", "InvalidToken"));
            InvalidTokenResourceText = grammar.GetOptionValue("pg.scanner.sr.InvalidToken.text", "Invalid token.");
        }

        public virtual void WriteStringArrayElements(ReadOnlyCollection<string> values, IndentedWriter writer)
        {
            if (values == null)
                throw new ArgumentNullException(nameof(values));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            int index = 0;
            foreach (string value in values)
            {
                WriteStringLiteral(value, writer);
                if (index++ != values.Count - 1)
                    writer.Write(',');
                writer.WriteLine();
            }
        }

        public virtual void WriteIntegerArrayElements(ReadOnlyCollection<int> values, IndentedWriter writer)
        {
            if (values == null)
                throw new ArgumentNullException(nameof(values));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            int max = 0;
            int min = 0;
            foreach (int value in values)
            {
                max = Math.Max(max, value);
                min = Math.Min(min, value);
            }
            int w1 = (max > 0x7FFF) ? 10 : (max > 0x7F) ? 6 : 4;
            int w2 = (int)Math.Log10(-min) + 2;
            int width = Math.Max(w1, w2);
            string fmt1 = string.Format("0x{{0:X{0}}}", w1 - 2);
            string fmt2 = string.Format("{{0,{0}}}", w2);
            string format = string.Format("{{0,{0}}}", width);
            int columns = (width > 6) ? 4 : 8;
            int index = 0;
            foreach (int value in values)
            {
                if (index % columns > 0)
                    writer.Write(' ');
                string str = string.Format(value >= 0 ? fmt1 : fmt2, value);
                writer.Write(format, str);
                if (index != values.Count - 1)
                    writer.Write(',');
                if (++index % columns == 0)
                    writer.WriteLine();
            }
            if (values.Count % columns != 0)
                writer.WriteLine();
        }

        public virtual void WriteDottedName(string name, IndentedWriter writer)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            Identifier id = Identifier.New(name);
            id.CheckDottedName();
            bool isFirst = true;
            foreach (Identifier item in id.Items)
            {
                writer.Write(isFirst ? "" : ".");
                WriteIdentifier(item, writer);
                isFirst = false;
            }
        }
    }
}
